package com.service.admin.trading.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.service.admin.support.MatchOrderCache;
import com.service.admin.support.RedissonUtils;
import com.service.admin.trading.service.TradingSpotOrderCheckService;
import com.service.admin.trading.service.TradingSpotOrderService;
import com.service.admin.trading.vo.StatisticNumVo;
import com.service.dao.enums.CommonStatusEnums;
import com.service.dao.mapper.TradingSpotOrderCheckMapper;
import com.service.dao.model.TradingSpotOrder;
import com.service.dao.model.TradingSpotOrderCheck;
import com.service.match.common.entity.MatchOrder;
import com.service.match.common.enums.TradingSpotEnum;
import com.service.match.common.utils.OrderCacheKeyUtil;
import com.service.utility.common.MathUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;

@Slf4j
@Service
public class TradingSpotOrderCheckServiceImpl extends ServiceImpl<TradingSpotOrderCheckMapper, TradingSpotOrderCheck> implements TradingSpotOrderCheckService {

    @Autowired
    private MatchOrderCache matchOrderCache;

    @Autowired
    private TradingSpotOrderService spotOrderService;

    @Autowired
    private RedissonUtils redissonUtils;

    @Override
    public void checkSportOrder(String symbol) {
        String symbolTemp = symbol.replace("-", "/");
        List<Integer> spotStatusList = Lists.newArrayList(0, 1);

        //检测买盘
        check(symbolTemp, true, spotStatusList);

        //账本侧检测数据
        checkOrderBook(symbolTemp, true, spotStatusList);

        //检测卖盘
        check(symbolTemp, false, spotStatusList);

        //账本侧检测数据
        checkOrderBook(symbolTemp, false, spotStatusList);
    }

    private void check(String symbol, boolean isBuy, List<Integer> spotStatusList) {
        log.info("盘口侧对比数据:{}", symbol);
//        Map<BigDecimal, BigDecimal> priceMatch = matchOrderCache.getPriceCache(OrderCacheKeyUtil.getMatchKey(symbol, isBuy));
        Map<BigDecimal, BigDecimal> priceMatch = redissonUtils.getMatchMap(OrderCacheKeyUtil.getMatchKey(symbol, isBuy));
        Date currentDate = new Date();
        TradingSpotEnum.TradeType tradeType = isBuy ? TradingSpotEnum.TradeType.BUY : TradingSpotEnum.TradeType.SELL;
        priceMatch.keySet().parallelStream().forEach(item -> {
            BigDecimal lastNum = priceMatch.get(item);
            log.info("price:{}, 查询账本,剩余数量:{}", item, lastNum);
            String key = OrderCacheKeyUtil.getOrderBookWithPriceKey(symbol, isBuy, item);
//            List<MatchOrder> orderBookList = matchOrderCache.getOrderBookByKey(key);
            List<MatchOrder> orderBookList = redissonUtils.getOrderBookList(key);
            StatisticNumVo orderBookResult = totalUnFinishNum(orderBookList);
            log.info("key:{},查询账本,剩余数量:{}", key, orderBookResult);
            StatisticNumVo databaseResult = totalUnDealNum(symbol, item, tradeType, spotStatusList);
            log.info("key:{},查询数据库剩余数量:{}", key, databaseResult);
            saveValidData(symbol, item, tradeType, lastNum, orderBookResult, databaseResult, currentDate);
        });
    }

    private void checkOrderBook(String symbol, boolean isBuy, List<Integer> spotStatusList) {
        Date currentDate = new Date();
        log.info("账本侧对比数据:{}", symbol);
        Set<String> keySet = matchOrderCache.patternKeys(symbol, isBuy);
//        Map<BigDecimal, BigDecimal> priceMatch = matchOrderCache.getPriceCache(OrderCacheKeyUtil.getMatchKey(symbol, isBuy));
        Map<BigDecimal, BigDecimal> priceMatch = redissonUtils.getMatchMap(OrderCacheKeyUtil.getMatchKey(symbol, isBuy));
        TradingSpotEnum.TradeType tradeType = isBuy ? TradingSpotEnum.TradeType.BUY : TradingSpotEnum.TradeType.SELL;
        keySet.parallelStream().forEach(item -> {
//            List<MatchOrder> orderBookList = matchOrderCache.getOrderBookByKey(item);
            List<MatchOrder> orderBookList = redissonUtils.getOrderBookList(item);
            StatisticNumVo orderBookResult = totalUnFinishNum(orderBookList);
            log.info("key:{},查询账本,剩余数量:{}", item, orderBookResult);
            BigDecimal priceKey = BigDecimal.ZERO;
            if (orderBookResult.getUnFinishNum().compareTo(BigDecimal.ZERO) > 0) {
                priceKey = OrderCacheKeyUtil.dealPrice2BigDecimal(orderBookList.get(0).getOrderPrice());
            }
            BigDecimal lastNum = priceMatch.getOrDefault(priceKey, BigDecimal.ZERO);
            log.info("price:{}, 查询盘口，剩余数量:{}", priceKey, lastNum);

            StatisticNumVo databaseResult = totalUnDealNum(symbol, priceKey, tradeType, spotStatusList);
            log.info("key:{},查询数据库剩余数量:{}", priceKey, databaseResult);

            saveValidData(symbol, priceKey, tradeType, lastNum, orderBookResult, databaseResult, currentDate);
        });
    }

    private void saveValidData(String symbol, BigDecimal price, TradingSpotEnum.TradeType tradeType, BigDecimal lastNum, StatisticNumVo orderBookResult, StatisticNumVo databaseResult, Date currentDate) {
        if (!isEqual(lastNum,orderBookResult.getUnFinishNum()) && !isEqual(lastNum,databaseResult.getUnFinishNum())
                || !isEqual(orderBookResult.getSurplusFrozen(),databaseResult.getSurplusFrozen())) {
            log.error("价格:{}的交易数据错误", price);
            save(TradingSpotOrderCheck.builder()
                    .symbol(symbol)
                    .checkStatus(CommonStatusEnums.INVALID.getCode())
                    .tradeType(tradeType.getCode())
                    .orderPrice(price)
                    .handicapNum(lastNum)
                    .orderBookNum(orderBookResult.getUnFinishNum())
                    .orderBookFrozen(orderBookResult.getSurplusFrozen())
                    .orderUnfinishedNum(databaseResult.getUnFinishNum())
                    .orderFrozen(databaseResult.getSurplusFrozen())
                    .createTime(currentDate)
                    .build());
        } else {
            log.info("价格:{}的交易数据正确", price);
        }
    }

    private boolean isEqual(BigDecimal source, BigDecimal source2){
        return MathUtil.keep8BitDecimalNormal(source).compareTo(MathUtil.keep8BitDecimalNormal(source2)) == 0;
    }

    public StatisticNumVo totalUnFinishNum(List<MatchOrder> orderBookList) {
        if (ObjectUtils.isEmpty(orderBookList)) {
            return StatisticNumVo.builder().surplusFrozen(BigDecimal.ZERO).unFinishNum(BigDecimal.ZERO).build();
        }
        AtomicReference<BigDecimal> totalUnFinishNum = new AtomicReference<>(BigDecimal.ZERO);
        AtomicReference<BigDecimal> totalSurplusFrozen = new AtomicReference<>(BigDecimal.ZERO);
        orderBookList.parallelStream().forEach(item -> {
            totalUnFinishNum.getAndAccumulate(item.getUnFinishNumber(), BigDecimal::add);
            totalSurplusFrozen.getAndAccumulate(item.getSurplusFrozen(), BigDecimal::add);
        });
        return StatisticNumVo.builder().surplusFrozen(totalSurplusFrozen.get()).unFinishNum(totalUnFinishNum.get()).build();
    }

    public StatisticNumVo totalUnDealNum(String symbol, BigDecimal price, TradingSpotEnum.TradeType tradeType, List<Integer> spotStatusList) {

        List<TradingSpotOrder> tradingSpotOrders = listSpotOrder(symbol, price, tradeType, spotStatusList);

        if (ObjectUtils.isEmpty(tradingSpotOrders)) {
            return StatisticNumVo.builder().surplusFrozen(BigDecimal.ZERO).unFinishNum(BigDecimal.ZERO).build();
        }
        AtomicReference<BigDecimal> totalUnFinishNum = new AtomicReference<>(BigDecimal.ZERO);
        AtomicReference<BigDecimal> totalSurplusFrozen = new AtomicReference<>(BigDecimal.ZERO);
        tradingSpotOrders.parallelStream().forEach(item -> {
            BigDecimal unDeal = item.getTradeNum().subtract(item.getDealNum());
            totalUnFinishNum.getAndAccumulate(unDeal, BigDecimal::add);
            totalSurplusFrozen.getAndAccumulate(item.getSurplusFrozen(), BigDecimal::add);
        });

        return StatisticNumVo.builder().surplusFrozen(totalSurplusFrozen.get()).unFinishNum(totalUnFinishNum.get()).build();

    }

    private List<TradingSpotOrder> listSpotOrder(String symbol, BigDecimal price, TradingSpotEnum.TradeType tradeType, List<Integer> spotStatusList){
       return spotOrderService.list(new QueryWrapper<TradingSpotOrder>().lambda()
                .eq(TradingSpotOrder::getSymbol, symbol)
                .eq(TradingSpotOrder::getOrderPrice, price)
                .eq(TradingSpotOrder::getTradeType, tradeType.getCode())
                .in(TradingSpotOrder::getSpotStatus, spotStatusList));
    }

}
